/* Copyright 2009 (c) ENSI de Bourges
 * 		88 boulevard Lahitolle, 18020 Bourges Cedex, France
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * \file application-data.c
 * \brief Source code for the application's functions
 * \author Steve Dodier <sidnioulz@gmail.com>
 *
 * This file contains functions manipulating LiveSecurity instances.
 */
#include <errno.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <stdlib.h>

#include "application-data.h"
#include "frame-private.h"
#include "table-utilities.h"



int frame_list_add (GList **list, frame_t *newelem)
{
	ls_instance_t	*inst	= livesecurity_instance ();

	*list = g_list_prepend (*list, newelem);

	return table_add (inst->table, newelem);
}



int frame_list_remove (GList **list, frame_t *elem)
{
	GList			*elemptr;
	ls_instance_t	*inst	= livesecurity_instance ();

	elemptr = g_list_find (*list, elem);
	
	if (elemptr) {
		int x =	table_remove (inst->table, elem);
		frame_free (elem);
		*list = g_list_remove (*list, elem);
		return x;
	} else {
		return -1;
	}
}



/** \fn static void machine_free_func (gpointer, gpointer)
 * \brief Frees a machine_t struct (GFunc version)
 *
 * \warning This is a private function.
 *
 * This function is a machine_free alias, that can be used as a GFunc.
 * @param[out] data the machine to free
 * @param[out] user_data a NULL gpointer
 */
static void frame_free_func (gpointer data, gpointer user_data)
{
	//TODO table management ?
	frame_free ((frame_t *) data);	
}



void frame_list_free (GList *list)
{
	if (list == NULL)
		return;

	g_list_foreach (list, frame_free_func, NULL);
	g_list_free (list);
}



/** \fn ls_instance_t *livesecurity_new ()
 * \brief Creates a new LiveSecurity instance
 *
 * \warning This is a private function.
 *
 * This function creates a new LiveSecurity instance, which means load a
 * GtkBuilder for the main GUI, load a table that will welcome frames, create an
 * empty list of frames and load a list of available machines.
 * @return a valid LiveSecurity instance
 */
static ls_instance_t *livesecurity_new ()
{
	char				*checkout	= NULL;
	char				*envbuff	= NULL;
	char				*path		= NULL;
	const gchar* const	*data_dirs	= g_get_system_data_dirs ();
	GError				*err		= NULL;
	int					i			= 0;
	ls_instance_t		*inst		= g_malloc (sizeof (ls_instance_t));
	struct stat			buff;


	// Initialising the config folder
	envbuff = g_strdup (getenv (ENV_CONFIG)); //dup'ing now because getenv is not reentrant
	if (envbuff)
		inst->config_dir = envbuff;
	else
		inst->config_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_config_dir ());
	if (g_stat (inst->config_dir, &buff) == 0) {
		if (!S_ISDIR (buff.st_mode)) {
			g_free (inst->config_dir);
			inst->config_dir = NULL;
			g_warning ("livesecurity_new: The config directory is not an actual folder.");			
		}
	} else {
		g_free (inst->config_dir);
		inst->config_dir = NULL;
		//g_warning ("livesecurity_new: Could not stat the config directory (%s)", g_strerror (errno));
	}
	if (inst->config_dir == NULL) {
		if (g_get_user_config_dir () != NULL)
			inst->config_dir = g_strdup_printf ("%s/"APP_NAME_LOWER, g_get_user_config_dir ());
		else
			inst->config_dir = g_strdup ("/tmp/"APP_NAME_LOWER);
		if (g_mkdir_with_parents (inst->config_dir, 0777) != 0) {
			g_warning ("livesecurity_new: Attempted to use %s as a config directory but it failed (%s)",
						inst->config_dir,
						g_strerror (errno));
			g_free (inst->config_dir);
			inst->config_dir = NULL; // Avoid a double free error on livesecurity_instance_free
		} else {
			g_print ("Created the %s directory for config.\n", inst->config_dir);
		}
	}


	// Initialising the machines directory (~/.config/.../machines/)
	envbuff = g_strdup (getenv (ENV_MACHINES));
	if (envbuff)
		inst->machines_dir = envbuff;
	else
		inst->machines_dir = g_strdup_printf ("%s/"APP_NAME_LOWER"/"MACHINES_DIR, g_get_user_config_dir ());
	if (g_stat (inst->machines_dir, &buff) == 0) {
		if (!S_ISDIR (buff.st_mode)) {
			g_free (inst->machines_dir);
			inst->machines_dir = NULL;
			g_warning ("livesecurity_new: The machines directory is not an actual folder.");			
		}
	} else {
		g_free (inst->machines_dir);
		inst->machines_dir = NULL;
		//g_warning ("livesecurity_new: Could not stat the machines directory (%s)", g_strerror (errno));
	}
	if (inst->machines_dir == NULL) {
		if (g_get_user_config_dir () != NULL)
			inst->machines_dir = g_strdup_printf ("%s/"APP_NAME_LOWER"/"MACHINES_DIR, g_get_user_config_dir ());
		else
			inst->machines_dir = g_strdup ("/tmp/"APP_NAME_LOWER"/"MACHINES_DIR);
		if (g_mkdir_with_parents (inst->machines_dir, 0777) != 0) {
			g_warning ("livesecurity_new: Attempted to use %s as a machines directory but it failed (%s)",
						inst->machines_dir,
						g_strerror (errno));
			g_free (inst->machines_dir);
			inst->machines_dir = NULL; // Avoid a double free error on livesecurity_instance_free
		} else {
			g_print ("Created the %s directory for machines.\n", inst->machines_dir);
		}
	}


	// Initialising the data directory (/usr/share/data/...)
	envbuff = g_strdup (getenv (ENV_DATA));
	if (envbuff)
		inst->data_dir = envbuff;
	else
		inst->data_dir = NULL;
	while (!inst->data_dir && data_dirs[i]) {
		checkout = g_strdup_printf ("%s/"APP_NAME_LOWER, data_dirs[i]);
		if (g_stat (checkout, &buff) == 0) {
			if (S_ISDIR (buff.st_mode))
				inst->data_dir = checkout;
			else
				g_free (checkout);
		} else {
			g_free (checkout);
		}
		i++;
	}
	if (inst->data_dir == NULL) {
		inst->data_dir = g_strdup ("/tmp/"APP_NAME_LOWER"/"DATA_DIR);
		if (g_mkdir_with_parents (inst->data_dir, 0777) != 0)
			g_error ("livesecurity_new: No data directory, aborting (%s)",
						g_strerror (errno));
	}


	// Main GUI's GtkBuilder
	inst->builder = gtk_builder_new ();
	path = g_strdup_printf ("%s/window.ui", inst->data_dir);
	gtk_builder_add_from_file (inst->builder, path, &err);
	g_free (path);
	if (err) {
		g_error ("livesecurity_new: %s", err->message);
		g_clear_error (&err);
		exit (EXIT_FAILURE);
	}


	// Data related to the GtkTable used for storing frames
	inst->table = gtk_table_new (1, 1, FALSE);
	gtk_table_set_row_spacings (GTK_TABLE (inst->table), 10);
	gtk_table_set_col_spacings (GTK_TABLE (inst->table), 10);

	for (i=0; i<LAST_SIZE_RATIO; i++)
		inst->children_size[i] = 0;

	inst->slot_data = g_malloc (sizeof (frame_t*));
	inst->slot_data[0] = NULL;

	
	// Machines and frames list
	inst->machines_list = machine_list_new (inst->machines_dir);
	inst->frames_list = NULL;


	// Application settings
	inst->settings = settings_load (inst->config_dir, inst->data_dir);
	if (inst->settings == NULL) {
		g_error ("livesecurity_new: Couldn't load the settings.");
		exit (EXIT_FAILURE);
	}

	return inst;
}



/** \fn ls_instance_t *_livesecurity_instance (int)
 * \brief Returns a pointer to the current LiveSecurity instance
 *
 * \warning You should not call this function unless you just freed the instance
 *
 * This function returns a pointer to the current instance of LiveSecurity (it
 * is implemented as a ls_instance_t static struct). If the instance
 * doesn't exist, it is created beforehand.
 * @param[in] setnull if not 0, then the instance will be set to NULL instead of returned
 * @return the current LiveSecurity instance, properly initialised
 */
static ls_instance_t *_livesecurity_instance (int setnull)
{
	static ls_instance_t *params = NULL;

	if (setnull)
		params = NULL;
	else if (params == NULL)
		params = livesecurity_new ();

	return params;
}



ls_instance_t *livesecurity_instance ()
{
	return _livesecurity_instance (0);
}



void livesecurity_instance_free ()
{
	ls_instance_t	*inst	= livesecurity_instance ();

	settings_save (inst->settings);
	g_free (inst->settings);

	frame_list_free (inst->frames_list);
	machine_list_free (inst->machines_list);
	// The table has been freed when the main window was destroyed.
	// This means its cols/rows data must be saved somewhere.
	g_object_unref (inst->builder);
	free (inst->slot_data);

	g_free (inst->data_dir);
	g_free (inst->config_dir);
	g_free (inst->machines_dir);

	g_free (inst);
	_livesecurity_instance (1); // set the instance to NULL
}
